package com.jay.media.music.core.audio

import android.annotation.SuppressLint
import android.media.*
import android.util.Log
import com.jay.media.kit.LogHelper
import java.io.FileOutputStream

/**
 * @author xuzhou4
 * @date 2022/1/28
 */
class AudioRecorderUtils {

    companion object {
        const val TAG = "AudioRecorderUtils"
        const val AUDIO_SOURCE = MediaRecorder.AudioSource.MIC
        const val SAMPLE_RATE = 44100
        const val CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO
        const val AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT
    }

    private var path: String? = null
    private var audioSource = AUDIO_SOURCE
    private var sampleRate = SAMPLE_RATE
    private var channelConfig = CHANNEL_CONFIG
    private var channelCount = 2
    private var audioFormat = AUDIO_FORMAT
    private var minBufferSize = 0

    private var timeoutUs = 0L

    private lateinit var audioRecord: AudioRecord
    private var audioRecordThread: Thread? = null

    @SuppressLint("MissingPermission")
    fun startRecord(path: String) {
        Log.d(TAG, "startRecord() called with: path = $path")
        this.path = path
        minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)
        audioRecord =
            AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, minBufferSize)
        audioRecord.startRecording()

        audioRecordThread = Thread(mRecordRunnable)
        audioRecordThread?.start()
    }

    private var mRecordRunnable = Runnable {
        LogHelper.d(TAG, "record thread start")
        val fos = FileOutputStream(path)
        val audioEncoder = createAudioEncoder()

        val byteArray = ByteArray(minBufferSize / 2)

        val bufferInfo = MediaCodec.BufferInfo()
        while (audioRecord.recordingState == AudioRecord.RECORDSTATE_RECORDING) {
            var readSize = audioRecord.read(byteArray, 0, minBufferSize / 2)
            LogHelper.d(TAG, "read audio size : $readSize")

            if (readSize > 0) {
                val dequeueInputBuffer = audioEncoder.dequeueInputBuffer(timeoutUs)
                if (dequeueInputBuffer >= 0) {
                    val inputBuffer = audioEncoder.getInputBuffer(dequeueInputBuffer)
                    Log.d(TAG, "capacity : ${inputBuffer?.capacity()}")
                    inputBuffer?.put(byteArray, 0, readSize)

                    audioEncoder.queueInputBuffer(dequeueInputBuffer, 0, readSize, 0, 0)
                }

                var dequeueOutputBuffer = audioEncoder.dequeueOutputBuffer(bufferInfo, timeoutUs)
                while (dequeueOutputBuffer >= 0) {
                    val outputBuffer = audioEncoder.getOutputBuffer(dequeueOutputBuffer)
                    val bytes = ByteArray(bufferInfo.size + 7)
                    addADTStoPacket(bytes, bytes.size)
                    outputBuffer?.get(bytes, 7, bufferInfo.size)
                    fos.write(bytes)
                    fos.flush()

                    audioEncoder.releaseOutputBuffer(dequeueOutputBuffer, false)

                    dequeueOutputBuffer = audioEncoder.dequeueOutputBuffer(bufferInfo, timeoutUs)
                }
            }
        }
        LogHelper.d(TAG, "record thread finish")
    }

    private fun addADTStoPacket(packet: ByteArray, packetLen: Int) {
        val profile = 2 //AAC LC
        val freqIdx = 4 //44.1KHz
        val chanCfg = channelCount //CPE
        packet[0] = 0xFF.toByte()
        packet[1] = 0xF9.toByte()
        packet[2] = ((profile - 1 shl 6) + (freqIdx shl 2) + (chanCfg shr 2)).toByte()
        packet[3] = ((chanCfg and 3 shl 6) + (packetLen shr 11)).toByte()
        packet[4] = (packetLen and 0x7FF shr 3).toByte()
        packet[5] = ((packetLen and 7 shl 5) + 0x1F).toByte()
        packet[6] = 0xFC.toByte()
    }

    fun stopRecord() {
        audioRecord.stop()
    }

    private fun createAudioEncoder(): MediaCodec {
        val audioFormat =
            MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_RAW, sampleRate, channelCount)
        audioFormat.setInteger(
            MediaFormat.KEY_AAC_PROFILE,
            MediaCodecInfo.CodecProfileLevel.AACObjectLC
        )
        audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 9600)
        val encoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC)
        encoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
        encoder.start()
        return encoder
    }
}